﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

using Please_starve.Common.Managers;
using Nuclex.Input;

namespace Please_starve
{
    public static class InputHandler
    {
        public static InputManager InputManager;

        /// <summary>
        /// The actions that are possible within the game.
        /// </summary>
        public enum Action
        {
            MoveCharacterUp,
            MoveCharacterDown,
            MoveCharacterLeft,
            MoveCharacterRight,
            ZoomIn,
            ZoomOut,
            LeftClick,
            RightClick,

            OpenChat,
            LeftArrow,
            RightArrow,
            Delete,
        }

        /// <summary>
        /// Reset the action maps to their default values.
        /// Uses Action to generate action on keypress
        /// </summary>
        public static void ResetActionMaps()
        {
            actionMaps = new ActionMap[Enum.GetValues(typeof(Action)).Length];

            addKeyToAction(Action.MoveCharacterUp, Keys.W);
            addKeyToAction(Action.MoveCharacterLeft, Keys.A);
            addKeyToAction(Action.MoveCharacterDown, Keys.S);
            addKeyToAction(Action.MoveCharacterRight, Keys.D);

            addKeyToAction(Action.MoveCharacterUp, Keys.Up);
            addKeyToAction(Action.MoveCharacterLeft, Keys.Left);
            addKeyToAction(Action.MoveCharacterDown, Keys.Down);
            addKeyToAction(Action.MoveCharacterRight, Keys.Right);

            addKeyToAction(Action.ZoomIn, MouseButton.ScrollUp);
            addKeyToAction(Action.ZoomOut, MouseButton.ScrollDown);

            //chat
            addKeyToAction(Action.OpenChat, Keys.Enter);
            addKeyToAction(Action.LeftArrow, Keys.Left);
            addKeyToAction(Action.RightArrow, Keys.Right);
            addKeyToAction(Action.Delete, Keys.Delete);

            addKeyToAction(Action.LeftClick, MouseButton.Left);
            addKeyToAction(Action.RightClick, MouseButton.Right);
        }

        static InputHandler()
        {
            ResetActionMaps();
        }


        /// <summary>
        /// Mouse button types
        /// </summary>
        public enum MouseButton
        {
            None,
            Left,
            Right,
            Middle,
            ScrollUp,
            ScrollDown,
        }

        public delegate void MouseEvent(MouseButton button);
        public static MouseEvent OnMousePressed;
        public static MouseEvent OnMouseRelease;
        public static MouseEvent OnMouseTriggered;


        public static Point MousePosScreen
        {
            get
            {
                return new Point(currentMouseState.X, currentMouseState.Y);
            }
        }

        public static Rectangle MouseRectangle
        {
            get
            {
                return new Rectangle(currentMouseState.X, currentMouseState.Y, 1, 1);
            }
        }

        private static KeyboardState currentKeyboardState;
        private static KeyboardState previousKeyboardState;

        //private static Nuclex.Input.Devices.IKeyboard keyBoard;
        //public static Nuclex.Input.Devices.IKeyboard KeyBoard
        //{
        //    get { return keyBoard; }
        //}

        public static KeyboardState CurrentKeyboardState
        {
            get { return currentKeyboardState; }
        }

        private static MouseState currentMouseState;
        private static MouseState previousMouseState;

        public static MouseState CurrentMouseState
        {
            get { return currentMouseState; }
        }

        private static int currentScroll;
        private static int previousScroll;

        public static int CurrentScroll
        {
            get { return currentScroll; }
        }

        private sealed class ActionMap
        {
            /// <summary>
            /// List of Keyboard controls to be mapped to a given action.
            /// </summary>
            public List<Keys> keyboardKeys = new List<Keys>();
            public MouseButton? mouseButton = null;
            public MouseButton? mouseButton2 = null;
        }

        /// <summary>
        /// The action mappings for the game.
        /// </summary>
        private static ActionMap[] actionMaps;

        private static void addKeyToAction(Action action, Keys key)
        {
            int index = (int)action;
            if (actionMaps[index] == null)
            {
                actionMaps[index] = new ActionMap();
            }
            actionMaps[index].keyboardKeys.Add(key);
        }

        private static void addKeyToAction(Action action, MouseButton button)
        {
            int index = (int)action;
            if (actionMaps[index] == null)
            {
                actionMaps[index] = new ActionMap();
            }
            actionMaps[index].mouseButton = button;

        }

        private static void addKeyToAction(Action action, MouseButton button, MouseButton button2)
        {
            int index = (int)action;
            if (actionMaps[index] == null)
            {
                actionMaps[index] = new ActionMap();
            }
            actionMaps[index].mouseButton = button;
            actionMaps[index].mouseButton2 = button2;
        }

        /// <summary>
        /// Check if an action has been pressed.
        /// </summary>
        public static bool IsActionPressed(Action action)
        {
            return IsActionMapPressed(actionMaps[(int)action]);
        }

        /// <summary>
        /// Check if an action was just performed in the most recent update.
        /// </summary>
        public static bool IsActionTriggered(Action action)
        {
            return IsActionMapTriggered(actionMaps[(int)action]);
        }

        /// <summary>
        /// Checks if an action was just stopped.
        /// TODO : Add all the other actions.
        /// Actions currently in: Select released
        /// </summary>
        public static bool IsActionReleased(Action action)
        {
            return IsActionMapReleased(actionMaps[(int)action], action);
        }

        /// <summary>
        /// Works on keyboard
        /// Should work like removing from an input field (one char first, then the rest)
        /// </summary>
        /// <param name="action"></param>
        /// <param name="gameTime"></param>
        /// <returns></returns>
        public static bool IsActionPressedDelayed(Action action, GameTime gameTime)
        {
            return IsActionMapPressedDelayed(actionMaps[(int)action], action, gameTime);
        }

        /// <summary>
        /// Check if an action map has been pressed.
        /// </summary>
        private static bool IsActionMapPressed(ActionMap actionMap)
        {
            //double mouse button usage
            if ((actionMap.mouseButton == MouseButton.Left) &&
                (actionMap.mouseButton2 == MouseButton.Right) &&
                (currentMouseState.LeftButton == ButtonState.Pressed) &&
                (currentMouseState.RightButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.Left) &&
                (actionMap.mouseButton2 == null) &&
                (currentMouseState.LeftButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.Right) &&
                (actionMap.mouseButton2 == null) &&
                (currentMouseState.RightButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.Middle) &&
                (actionMap.mouseButton2 == null) &&
                (currentMouseState.MiddleButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.ScrollDown) &&
                (actionMap.mouseButton2 == null) &&
                (currentScroll < previousScroll))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.ScrollUp) &&
                (actionMap.mouseButton2 == null) &&
                (currentScroll > previousScroll))
            {
                return true;
            }

            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (currentKeyboardState.IsKeyDown(actionMap.keyboardKeys[i]))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Checks if an action just finished.
        /// </summary>
        private static bool IsActionMapReleased(ActionMap actionmap, Action action)
        {
            if ((actionmap.mouseButton == MouseButton.Left) &&
                (currentMouseState.LeftButton == ButtonState.Released)
                && previousMouseState.LeftButton == ButtonState.Pressed)
            {
                return true;
            }

            return false;
        }

        static Action prevAction = Action.OpenChat;
        static float tick = 0;
        static float delay = 0.02f;

        private static bool IsActionMapPressedDelayed(ActionMap actionMap, Action action, GameTime gameTime)
        {
            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if (currentKeyboardState.IsKeyDown(actionMap.keyboardKeys[i]))
                {
                    if (prevAction != action)
                    {
                        tick = 0;
                        prevAction = action;
                        delay = 0.5f;
                        return true;
                    }
                    else
                    {
                        tick += (float)gameTime.ElapsedGameTime.TotalSeconds;

                        if (tick > delay)
                        {
                            delay = 0.02f;
                            tick = 0;
                            prevAction = action;
                            return true;
                        }

                        prevAction = action;
                        return false;
                    }
                }
                else if (currentKeyboardState.IsKeyUp(actionMap.keyboardKeys[i]) && prevAction == action)
                {
                    tick = 0;
                    prevAction = Action.OpenChat;
                    return false;
                }
            }

            return false;
        }


        /// <summary>
        /// Check if an action map has been triggered this frame.
        /// </summary>
        private static bool IsActionMapTriggered(ActionMap actionMap)
        {

            if ((actionMap.mouseButton == MouseButton.Left) &&
                (currentMouseState.LeftButton == ButtonState.Pressed) &&
                !(previousMouseState.LeftButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.Right) &&
                (currentMouseState.RightButton == ButtonState.Pressed) &&
                !(previousMouseState.RightButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.Middle) &&
                (currentMouseState.MiddleButton == ButtonState.Pressed) &&
                !(previousMouseState.MiddleButton == ButtonState.Pressed))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.ScrollDown) &&
                (currentScroll < previousScroll))
            {
                return true;
            }

            if ((actionMap.mouseButton == MouseButton.ScrollUp) &&
                (currentScroll > previousScroll))
            {
                return true;
            }

            for (int i = 0; i < actionMap.keyboardKeys.Count; i++)
            {
                if ((currentKeyboardState.IsKeyDown(actionMap.keyboardKeys[i])) &&
                    (!previousKeyboardState.IsKeyDown(actionMap.keyboardKeys[i])))
                {
                    return true;
                }
            }
            return false;
        }

        static bool leftHold;
        static bool rightHold;
        static bool middleHold;
        private static void mouseUpdate()
        {

            //Pressed
            if (currentMouseState.LeftButton == ButtonState.Pressed)
            {
                leftHold = true;
                if (OnMousePressed != null)
                    OnMousePressed(MouseButton.Left);
            }

            if (currentMouseState.RightButton == ButtonState.Pressed)
            {
                rightHold = true;
                if (OnMousePressed != null)
                    OnMousePressed(MouseButton.Right);
            }

            if (currentMouseState.MiddleButton == ButtonState.Pressed)
            {
                middleHold = true;
                if (OnMousePressed != null)
                    OnMousePressed(MouseButton.Middle);
            }

            //Released
            if (leftHold && currentMouseState.LeftButton == ButtonState.Released)
            {
                leftHold = false;
                if (OnMouseRelease != null)
                    OnMouseRelease(MouseButton.Left);
            }

            if (rightHold && currentMouseState.RightButton == ButtonState.Released)
            {
                rightHold = false;
                if (OnMouseRelease != null)
                    OnMouseRelease(MouseButton.Right);
            }

            if (middleHold && currentMouseState.MiddleButton == ButtonState.Released)
            {
                middleHold = false;
                if (OnMouseRelease != null)
                    OnMouseRelease(MouseButton.Middle);
            }


            //Trigger
            if (currentMouseState.LeftButton == ButtonState.Pressed && previousMouseState.LeftButton == ButtonState.Released)
            {
                if (OnMouseTriggered != null)
                    OnMouseTriggered(MouseButton.Left);
            }

            if (currentMouseState.RightButton == ButtonState.Pressed && previousMouseState.RightButton == ButtonState.Released)
            {
                if (OnMouseTriggered != null)
                    OnMouseTriggered(MouseButton.Right);
            }

            if (currentMouseState.MiddleButton == ButtonState.Pressed && previousMouseState.MiddleButton == ButtonState.Released)
            {
                if (OnMouseTriggered != null)
                    OnMouseTriggered(MouseButton.Middle);
            }

        }

        /// <summary>
        /// Updates the keyboard and mouse states
        /// </summary>
        public static void Update(GameTime gameTime)
        {
            // update the keyboard state
            previousKeyboardState = currentKeyboardState;

            currentKeyboardState = InputManager.GetKeyboard().GetState();

            previousMouseState = currentMouseState;
            currentMouseState = InputManager.GetMouse().GetState();

            mouseUpdate();

            previousScroll = currentScroll;
            currentScroll = currentMouseState.ScrollWheelValue;
        }

    }
}
